import pygame
import math
from pygame.locals import *
import sys

# 初始化pygame
pygame.init()

# 窗口参数
width, height = 800, 600
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption("Bouncing Ball in Rotating Hexagon")

# 颜色
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
BLUE = (0, 0, 255)

# 中心坐标
center_x = width // 2
center_y = height // 2

# 六边形参数
hex_radius = 200
num_sides = 6
angle = 0  # 初始角度
angular_speed = math.radians(30)  # 30度/秒

# 球的参数
ball_radius = 10
ball_x = center_x
ball_y = center_y - hex_radius * 0.5
ball_vx = 0.0
ball_vy = 0.0
gravity = 500  # 像素/秒²
restitution = 0.8  # 恢复系数
friction = 0.5  # 动摩擦系数

clock = pygame.time.Clock()
fps = 60

def get_hex_points(angle):
    points = []
    for i in range(num_sides):
        theta = angle + math.radians(60 * i)
        x = center_x + hex_radius * math.cos(theta)
        y = center_y + hex_radius * math.sin(theta)
        points.append((x, y))
    points.append(points[0])  # 闭合六边形
    return points

running = True
while running:
    dt = clock.tick(fps) / 1000.0  # 转换为秒

    for event in pygame.event.get():
        if event.type == QUIT:
            running = False

    # 更新旋转角度
    angle += angular_speed * dt
    angle %= 2 * math.pi

    # 获取六边形顶点
    hex_points = get_hex_points(angle)

    # 更新球的物理状态
    ball_vy += gravity * dt
    ball_vx *= 0.999
    ball_vy *= 0.999

    new_x = ball_x + ball_vx * dt
    new_y = ball_y + ball_vy * dt
    ball_x, ball_y = new_x, new_y

    # 碰撞检测和处理
    collision_occurred = False
    for i in range(num_sides):
        A = hex_points[i]
        B = hex_points[i+1]
        Ax, Ay = A
        Bx, By = B

        # 计算中点M和法线向量
        Mx = (Ax + Bx) / 2
        My = (Ay + By) / 2
        omx, omy = Mx - center_x, My - center_y
        length = math.hypot(omx, omy)
        if length == 0:
            continue
        N = (omx / length, omy / length)

        # 计算最近点Q
        APx, APy = ball_x - Ax, ball_y - Ay
        ABx, ABy = Bx - Ax, By - Ay
        ab_length_sq = ABx**2 + ABy**2
        if ab_length_sq == 0:
            continue
        t = (APx * ABx + APy * ABy) / ab_length_sq
        t = max(0, min(1, t))
        Qx = Ax + t * ABx
        Qy = Ay + t * ABy

        # 计算有符号距离
        signed_d = (ball_x - Ax) * N[0] + (ball_y - Ay) * N[1]
        dist = math.hypot(ball_x - Qx, ball_y - Qy)

        if dist < ball_radius and signed_d > 0:
            collision_occurred = True
            OQx, OQy = Qx - center_x, Qy - center_y
            v_wall_x = -angular_speed * OQy
            v_wall_y = angular_speed * OQx

            rel_vx = ball_vx - v_wall_x
            rel_vy = ball_vy - v_wall_y

            vn = rel_vx * N[0] + rel_vy * N[1]
            vn_x = vn * N[0]
            vn_y = vn * N[1]
            vt_x = rel_vx - vn_x
            vt_y = rel_vy - vn_y

            new_vn_x = -restitution * vn_x
            new_vn_y = -restitution * vn_y
            vt_x *= (1 - friction)
            vt_y *= (1 - friction)

            new_rel_vx = new_vn_x + vt_x
            new_rel_vy = new_vn_y + vt_y

            ball_vx = new_rel_vx + v_wall_x
            ball_vy = new_rel_vy + v_wall_y

            # 调整位置防止穿透
            penetration = ball_radius - dist
            ball_x -= N[0] * penetration
            ball_y -= N[1] * penetration
            break

    # 绘制
    screen.fill(WHITE)
    if len(hex_points) > 1:
        pygame.draw.lines(screen, BLUE, True, hex_points[:-1], 2)
    pygame.draw.circle(screen, RED, (int(ball_x), int(ball_y)), ball_radius)
    pygame.display.flip()

pygame.quit()
sys.exit()